﻿using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace UIElement
{
    [RequireComponent(typeof(ScrollRect))]
    public class ScrollGrid : ScrollBase
    {
        [SerializeField]
        ScrollRect m_ScrollRect;
        protected List<GameObject> m_ItemsPool = new List<GameObject>();
        List<GameObject> m_ShowItems = new List<GameObject>();
        //  列数
        [SerializeField]
        [Header("列")]
        int m_nColumn = 1;
        [SerializeField]
        [Header("自动对齐速度,多少秒对齐")]
        [Range(0.01f, 1)]
        float m_fSmoothSpeed = 0.1f;
        [SerializeField]
        [Header("多大速度算滚动停止，开始自动对齐")]
        float m_fScollEndSpeed = 100;

        Vector2 m_ItemSize;
        Vector2 m_ViewSize;
        int m_nFirstIndex = 0;
        int m_ViewRow = 0;
        bool m_bScrolling = false;
        bool m_bCheckingScrolling = false;
        float m_fEndPos = 0;
        // Start is called before the first frame update
        void Start()
        {
            m_ScrollRect = GetComponent<ScrollRect>();
            m_ScrollRect.onValueChanged.AddListener(OnScrollValueChanged);
            switch (m_ScrollDir)
            {
                case ScrollDir.Protrait:
                    m_ScrollRect.horizontal = false;
                    m_ScrollRect.vertical = true;
                    break;
                case ScrollDir.Landscape:
                    m_ScrollRect.horizontal = true;
                    m_ScrollRect.vertical = false;
                    break;
            }

            EventTrigger trigger = gameObject.GetOrAddComponent<EventTrigger>();
            EventTrigger.Entry entry = new EventTrigger.Entry();
            entry.eventID = EventTriggerType.EndDrag;
            entry.callback.AddListener((data) => OnEndDragDelegate((PointerEventData)data));
            trigger.triggers.Add(entry);
            EventTrigger.Entry startentry = new EventTrigger.Entry();
            startentry.eventID = EventTriggerType.BeginDrag;
            startentry.callback.AddListener((data) => OnBeginDragDelegate((PointerEventData)data));
            trigger.triggers.Add(startentry);
        }

        private void OnBeginDragDelegate(PointerEventData data)
        {
            m_bCheckingScrolling = false;
            m_bScrolling = false;
        }

        void MoveToRow(int row)
        {
            float v = 0;
            var contentSize = m_Content.GetComponent<RectTransform>().sizeDelta;

            switch (m_ScrollDir)
            {
                case ScrollDir.Protrait:
                    v = row * m_ItemSize.y;
                    m_fEndPos = 1 - v / (contentSize.y - m_ViewSize.y);
                    break;
                case ScrollDir.Landscape:
                    v = row * m_ItemSize.x;
                    m_fEndPos = v / (contentSize.x - m_ViewSize.x);
                    break;
            }
            m_bScrolling = true;

        }

        bool DoCheckScroll()
        {
            // 如果速度足够慢，就认为滚动停止了
            if (m_ScrollRect.velocity.sqrMagnitude < m_fScollEndSpeed)
            {
                DoAlign();
                m_bScrolling = true;
            }
            else
            {
                m_bCheckingScrolling = true;
            }
            return true;
        }
        private void OnEndDragDelegate(PointerEventData data)
        {
            DoCheckScroll();
        }
        void DoAlign()
        {
            int index = 0;
            Vector2 contentPosition = m_Content.GetComponent<RectTransform>().anchoredPosition;
            switch (m_ScrollDir)
            {
                case ScrollDir.Landscape:
                    index = Mathf.RoundToInt(-contentPosition.x / m_ItemSize.x);
                    break;
                case ScrollDir.Protrait:
                    index = Mathf.RoundToInt(contentPosition.y / m_ItemSize.y);
                    break;
            }
            MoveToRow(index);
            m_bCheckingScrolling = false;
        }
        void OnDestroy()
        {
            if (m_ScrollRect != null)
                m_ScrollRect.onValueChanged.RemoveListener(OnScrollValueChanged);
        }
        public override void SetCount(int count)
        {
            base.SetCount(count);
            m_nFirstIndex = -1;
            FreshAllItem();
        }
        internal void SetColumn(int v)
        {
            m_nColumn = v;
            InitMember();
            FreshAllItem();
        }
        void InitMember()
        {
            m_ItemSize = m_ItemProfab.GetComponent<RectTransform>().rect.size;
            m_ViewSize = GetComponent<RectTransform>().rect.size;
            Vector2 contentSize = Vector2.zero;
            int row = (int)MathF.Ceiling((float)ItemNum / (float)m_nColumn);
            switch (m_ScrollDir)
            {
                case ScrollDir.Protrait:
                    m_ViewRow = (int)MathF.Ceiling(m_ViewSize.y / m_ItemSize.y) + 1;
                    contentSize = new Vector2(m_nColumn * m_ItemSize.x, row * m_ItemSize.y);
                    break;
                case ScrollDir.Landscape:
                    m_ViewRow = (int)MathF.Ceiling(m_ViewSize.x / m_ItemSize.x) + 1;
                    contentSize = new Vector2(row * m_ItemSize.x, m_nColumn * m_ItemSize.y);
                    break;
            }
            m_Content.GetComponent<RectTransform>().sizeDelta = contentSize;
            m_nFirstIndex = -1;
        }
        public override void DoInit(int num, OnShowDelegate onShowItem, OnSelectDelegate onSelectItem)
        {
            base.DoInit(num, onShowItem, onSelectItem);
            InitMember();

            FreshAllItem();
        }
        void OnScrollValueChanged(Vector2 pos)
        {
            FreshAllItem();
        }

        // Update is called once per frame
        void Update()
        {
            if (m_bCheckingScrolling)
            {
                DoCheckScroll();
            }
            if (m_bScrolling)
            {
                switch (m_ScrollDir)
                {
                    case ScrollDir.Protrait:
                        m_ScrollRect.verticalNormalizedPosition = Mathf.Lerp(
                        m_ScrollRect.verticalNormalizedPosition,
                        m_fEndPos,
                        Time.deltaTime / m_fSmoothSpeed);
                        if (Mathf.Abs(m_ScrollRect.verticalNormalizedPosition - m_fEndPos) < 0.001f)
                        {
                            m_ScrollRect.verticalNormalizedPosition = m_fEndPos;
                            m_bScrolling = false;
                        }
                        break;
                    case ScrollDir.Landscape:
                        m_ScrollRect.horizontalNormalizedPosition = Mathf.Lerp(
                        m_ScrollRect.horizontalNormalizedPosition,
                        m_fEndPos,
                        Time.deltaTime / m_fSmoothSpeed);
                        if (Mathf.Abs(m_ScrollRect.horizontalNormalizedPosition - m_fEndPos) < 0.001f)
                        {
                            m_ScrollRect.horizontalNormalizedPosition = m_fEndPos;
                            m_bScrolling = false;
                        }
                        break;
                }
            }
        }

        // 根据当前位置刷新所有item
        public void FreshAllItem()
        {
            switch (m_ScrollDir)
            {
                case ScrollDir.Protrait:
                    FreshAllProtrait();
                    break;
                case ScrollDir.Landscape:
                    FreshAllLandscape();
                    break;
            }
        }

        private void FreshAllLandscape()
        {
            // 横向滚动示例
            Vector2 contentPosition = m_Content.GetComponent<RectTransform>().anchoredPosition;
            // 计算当然位置显示的是第几行
            int index = -(int)(contentPosition.x / m_ItemSize.x);
            if (index == m_nFirstIndex)
                return;
            // 总共能显示多少个
            int visibleNum = m_ViewRow * m_nColumn;
            m_ShowItems = new List<GameObject>(visibleNum);
            // 先隐藏所有列表项
            foreach (var item in m_ItemsPool)
            {
                item.SetActive(false);
            }
            int value = index * m_nColumn;
            // 先把不变的项赋值
            for (int i = 0; i < visibleNum; ++i)
            {
                // 如果超过最大值，就停止
                if (value + i >= ItemNum)
                    break;
                var item = GetExitItemByIndex(value + i);
                if (item != null)
                {
                    item.SetActive(true);
                }
                m_ShowItems.Add(item);
            }
            Vector2 zero = Vector2.zero;
            for (int i = 0; i < m_ShowItems.Count; ++i)
            {
                if (m_ShowItems[i] == null)
                {
                    m_ShowItems[i] = GetFreeItem();
                    m_ShowItems[i].SetActive(true);
                }
                zero.x = (index + i / m_nColumn) * m_ItemSize.x;
                zero.y = -i % m_nColumn * m_ItemSize.y;
                m_ShowItems[i].GetComponent<RectTransform>().anchoredPosition = zero;
                m_ShowItems[i].GetComponent<CommonListItem>().SetItemData(i + value);
            }
            m_nFirstIndex = index;
        }

        private void FreshAllProtrait()
        {

            // 垂直滚动示例
            Vector2 contentPosition = m_Content.GetComponent<RectTransform>().anchoredPosition;
            // 计算当然位置显示的是第几行
            int index = (int)(contentPosition.y / m_ItemSize.y);
            if (index == m_nFirstIndex)
                return;
            // 总共能显示多少个
            int visibleNum = m_ViewRow * m_nColumn;
            m_ShowItems = new List<GameObject>(visibleNum);
            // 先隐藏所有列表项
            foreach (var item in m_ItemsPool)
            {
                item.SetActive(false);
            }
            int value = index * m_nColumn;
            // 先把不变的项赋值
            for (int i = 0; i < visibleNum; ++i)
            {
                // 如果超过最大值，就停止
                if (value + i >= ItemNum)
                    break;
                var item = GetExitItemByIndex(value + i);
                if (item != null)
                {
                    item.SetActive(true);
                }
                m_ShowItems.Add(item);
            }
            Vector2 zero = Vector2.zero;
            for (int i = 0; i < m_ShowItems.Count; ++i)
            {
                if (m_ShowItems[i] == null)
                {
                    m_ShowItems[i] = GetFreeItem();
                    m_ShowItems[i].SetActive(true);
                }
                zero.x = i % m_nColumn * m_ItemSize.x;
                zero.y = -(index + i / m_nColumn) * m_ItemSize.y;
                m_ShowItems[i].GetComponent<RectTransform>().anchoredPosition = zero;
                m_ShowItems[i].GetComponent<CommonListItem>().SetItemData(i + value);
            }
            m_nFirstIndex = index;
        }

        GameObject GetFreeItem()
        {
            foreach (var item in m_ItemsPool)
            {
                if (item.activeInHierarchy == false)
                {
                    return item;
                }
            }
            var newItem = CreateItem();
            m_ItemsPool.Add(newItem);
            return newItem;
        }
        GameObject GetExitItemByIndex(int index)
        {
            foreach (var item in m_ItemsPool)
            {
                if (item.GetComponent<CommonListItem>().Value == index)
                {
                    return item;
                }
            }
            return null;

        }

    }
}
